package com.lagou.edu.factory.scanner.impl;

import com.lagou.edu.factory.scanner.ClassScanner;
import com.lagou.edu.factory.scanner.Matcher;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

/**
 * 包路径的类扫描器
 */
public class PackageClassScanner implements ClassScanner {
    /**
     * 扫描给定路径，找到满足要求的类
     *
     * @param path 给定的路径
     * @param matcher 匹配器
     * @return 满足要求的类
     */
    @Override
    public List<Class<?>> scan(String path, Matcher matcher) {
        List<Class<?>> matchedClasses = new ArrayList<>();

        try {
            File packagePath = resolvePackagePath(path);
            scan(packagePath, matcher, matchedClasses);
        } catch (IOException | ClassNotFoundException | URISyntaxException ex) {
            ex.printStackTrace();
        }

        return matchedClasses;
    }

    /**
     * 扫描给定的包路径
     *
     * @param packagePath 给定的包路径
     * @param matcher 匹配器
     * @param matchedClasses 存放匹配的类的列表
     */
    private void scan(File packagePath, Matcher matcher, List<Class<?>> matchedClasses)
            throws IOException, ClassNotFoundException, URISyntaxException {
        if (packagePath.isDirectory()) {
            File[] files = packagePath.listFiles();
            for (File file : files) {
                scan(file, matcher, matchedClasses);
            }

            return;
        }

        String file = packagePath.getCanonicalPath();
        if (!file.endsWith(".class")) {
            return;
        }

        Class<?> clz = loadClass(file);
        if (!matcher.isMatch(clz)) {
            return;
        }

        matchedClasses.add(clz);
    }

    /**
     * 加载类文件
     *
     * @param classFile 给定的类文件
     * @return 类对象
     */
    private Class<?> loadClass(String classFile) throws ClassNotFoundException, URISyntaxException, IOException {
        URL rootPath = getClass().getResource("/");
        File rootDir = new File(rootPath.toURI());

        String classPath = classFile.replace(rootDir.getCanonicalPath(), "")
                .replaceAll("\\\\|/", ".");
        if (classPath.startsWith(".")) {
            classPath = classPath.substring(1);
        }

        int idx = classPath.lastIndexOf(".class");
        String className = classPath.substring(0, idx);

        return Class.forName(className);
    }

    /**
     * 将给定的路径转换成包的路径
     *
     * @param path 给定的路径
     * @return 包的路径
     */
    private File resolvePackagePath(String path) throws URISyntaxException {
        URL rootPath = getClass().getResource("/");
        File rootDir = new File(rootPath.toURI());

        return new File(rootDir,
                path.replaceAll("\\.", "/"));
    }
}
